package com.cl.cloud.binlog.content;

import com.cl.cloud.binlog.annotation.BLEntity;
import com.cl.cloud.binlog.binlogEntity.BinLogEntityHandle;
import com.cl.cloud.binlog.factory.BinaryLogClientFactory;
import com.cl.cloud.binlog.factory.DefaultBinaryLogClientFactory;
import com.cl.cloud.binlog.filter.TableFilter;
import com.cl.cloud.binlog.handler.DistributedClientHandler;
import com.cl.cloud.binlog.handler.PositionHandler;
import com.cl.cloud.binlog.handler.RedisDistributedClientHandler;
import com.cl.cloud.binlog.properties.BinLogProperty;
import com.cl.core.common.exception.ServerException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @description: 上下文
 * @author: liuzijian
 * @time: 2022-07-08 11:11
 */
@Slf4j
public class BinLogApplicationContext implements DisposableBean, InitializingBean {

    //配置属性实体
    @Getter
    @Autowired
    private BinLogProperty binLogProperty;

    //客户端工厂
    @Getter
    @Lazy
    @Autowired
    private BinaryLogClientFactory clientFactory;

    //binlog位点处理器
    @Getter
    @Lazy
    @Autowired
    private PositionHandler positionHandler;

    //binlog分布式处理器
    @Lazy
    @Autowired
    private DistributedClientHandler distributedClientHandler;

    /**
     * 是否已初始化
     */
    private volatile boolean isInit;
    /**
     * 表过滤器（不在过滤器里的表不会进行处理 但仍然会监听）
     */
    @Getter
    private final TableFilter tableFilter = new TableFilter();

    /**
     * 上下文是否被销毁
     */
    public volatile boolean isDestory = false;

    private final ReentrantLock lock = new ReentrantLock();

    public BinLogApplicationContext() {
    }

    public void init() {
        if (!isInit) {
            lock.lock();
            try {
                if (!isInit) {
                    log.info("开始初始化binlog");
                    /*
                     * 初始化binlog的实体类 对带有{@link BLEntity}注解 同时 实现了{@link BinLogEntityHandle}的类进行扫描
                     */
                    initBLEntity();
                    /*
                     * 启动binlog分布式处理器
                     */
                    distributedClientHandler.start();
                    log.info("结束初始化binlog");
                    this.isInit = true;
                }
            } catch (Exception e) {
                log.error("binlog服务异常", e);
                clientFactory.close();
                System.exit(0);
            } finally {
                lock.unlock();
            }
        }
    }

    private void initBLEntity() {
        log.info("初始化binlog的实体类");
        try {
            PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = pathMatchingResourcePatternResolver.getResources(ClassUtils.convertClassNameToResourcePath(new StandardEnvironment().resolveRequiredPlaceholders("com.cl.cloud.binlog.binlogEntity")) + "/**/*.class");
            MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
            for (Resource res : resources) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(res);
                if (
                        metadataReader.getAnnotationMetadata().getAnnotations().isPresent(BLEntity.class) &&
                                Arrays.asList(metadataReader.getAnnotationMetadata().getInterfaceNames()).contains(BinLogEntityHandle.class.getName())
                ) {
                    Class<? extends BinLogEntityHandle> clazz = (Class<? extends BinLogEntityHandle>) ClassUtils.forName(metadataReader.getAnnotationMetadata().getClassName(), null);
                    this.tableFilter.registTable(clazz);
                }
            }
        } catch (Exception e) {
            throw new ServerException("初始化binlog实体类异常");
        }
        log.info("初始化binlog的实体类完毕");
    }

    @Override
    public void destroy() throws Exception {
        log.info("关闭binlog");
        isDestory = true;
        distributedClientHandler.close();
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }
}
